﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME      : FI.Net Application Framework
// COMPONENT ID     : ApplicationContext
// COMPONENT DESC   : Session based contextual data sharing.
//
// CREATED DATE/BY  : 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY :
// DATE/BY            SR#/CS/PM#/OTHERS      DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using FI.Core.ApplicationContexts.Configuration;
using FI.Core.Configuration;
using FI.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;

namespace FI.Core.ApplicationContexts
{
    /// <summary>
    ///The <see cref="ContextLocatorBase"/> which uses the <see cref="CallContext"/> as the context storage.
    /// </summary>
    [ConfigurationElement(typeof(CallContextLocatorData))]
    public class CallContextLocator : ContextLocatorBase
    {
        #region Fields
        [ThreadStatic]
        private static List<string> keys;
        #endregion

        #region Properties
        /// <summary>
        /// Gets the keys.
        /// </summary>
        /// <value>
        /// The keys.
        /// </value>
        public static IList<string> Keys
        {
            get { return keys ?? (keys = new List<string>()); }
        }
        #endregion

        #region Implement ContextLocator
        /// <summary>
        /// Get an existing context item by given key.
        /// </summary>
        /// <param name="key">The key of the <see cref="ContextItem"/> to get.</param>
        /// <returns>
        /// The <see cref="ContextItem"/> object to get.
        /// </returns>
        public override ContextItem GetContextItem(string key)
        {
            Guard.ArgumentNotNullOrEmpty(key, "key");
            return (CallContext.GetData(key) as ContextItem);
        }

        /// <summary>
        /// Add a new context item or use the new context item to override the exiting one.
        /// </summary>
        /// <param name="contextItem">The new <see cref="ContextItem"/> to set.</param>
        protected override void SetContextItemCore(ContextItem contextItem)
        {
            Guard.ArgumentNotNull(contextItem, "contextItem");

            CallContext.FreeNamedDataSlot(contextItem.Key);
            CallContext.SetData(contextItem.Key, contextItem);
            if (!Keys.Contains(contextItem.Key))
            {
                Keys.Add(contextItem.Key);
            }
        }

        /// <summary>
        /// Get all current context item collection.
        /// </summary>
        /// <returns>
        /// A <see cref="ContextItemCollection"/> containg all of the current context items.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
        public override ContextItemCollection GetCurrentContext()
        {
            ContextItemCollection context = new ContextItemCollection();
            foreach(var key in Keys)
            {
                ContextItem item = this.GetContextItem(key);
                if (null != item)
                {
                    context.Add(item);
                }
            }
            return context;
        }


        /// <summary>
        /// Clear the current context item collection.
        /// </summary>
        public override void Clear()
        {
            foreach (var key in Keys)
            {
                CallContext.FreeNamedDataSlot(key);
            }
            Keys.Clear();
        }

        /// <summary>
        /// Check if the context item of the given key exists.
        /// </summary>
        /// <param name="key">The key of the <see cref="ContextItem"/>.</param>
        /// <returns>
        /// true if the <see cref="ContextItem"/> already exists; otherwise, false.
        /// </returns>
        public override bool ContextItemExits(string key)
        {
            Guard.ArgumentNotNullOrEmpty(key, "key");
            return Keys.Contains(key);
        }
        #endregion
    }
}